/*
 * UserInterface.cpp
 *
 *  Created on: 26-12-2010
 *      Author: Przemek
 */

#include "UserInterface.hpp"
#include "gui/ClipArea.hpp"
#include "gui/ScrollBar.hpp"
#include "gui/TabbedPane.hpp"
#include "gui/Label.hpp"
#include "gui/ScrollableContainer.hpp"
#include "world/World.hpp"
#include "utils/VectorUtils.hpp"
#include "listener/AddElementToWorldMouseListener.hpp"
#include "listener/StartStopWorldKeyListener.hpp"

const float UserInterface::LEFT_PANEL_WIDTH = 200.0f;

UserInterface::UserInterface(const Dimension& baseWindowSize) :
	pressed(false), windowSize(baseWindowSize), selectElementMouseListener(
			new SelectElementMouseListener())
{
	leftPanel = createLeftPanel();
	rightPanel = createRightPanel();
	rootContainer.add(leftPanel);
	rootContainer.add(rightPanel);

	controlWithFocus = &rootContainer;
}

void UserInterface::windowSizeChanged(const Dimension& windowSize)
{
	rootContainer.setSize(windowSize);

	leftPanel->setBounds(geometry::Rectangle(Vector(0, 0), Dimension(LEFT_PANEL_WIDTH,
			windowSize.getHeight())));
	rightPanel->setBounds(geometry::Rectangle(Vector(LEFT_PANEL_WIDTH, 0.0f), Dimension(
			windowSize.getWidth() - LEFT_PANEL_WIDTH, windowSize.getHeight())));
}

void UserInterface::processMouseButtonEvent(const SDL_MouseButtonEvent& event)
{
	MouseListener::EventArguments arguments(Vector(event.x, event.y),
			MouseListener::EventArguments::NONE);
	if(event.button == 1)
	{
		arguments.setButton(MouseListener::EventArguments::LEFT);
	}
	if(event.button == 3)
	{
		arguments.setButton(MouseListener::EventArguments::RIGHT);
	}

	arguments.setPoint(switchFocusControlAndTransformToItsCoordinates(arguments.getPoint()));
	arguments.setSource(controlWithFocus);
	pressed = (event.type == SDL_MOUSEBUTTONDOWN);
	if(pressed)
	{
		controlWithFocus->mousePressed(arguments);
	}
	else
	{
		controlWithFocus->mouseReleased(arguments);
	}
}

Vector UserInterface::switchFocusControlAndTransformToItsCoordinates(const Vector& point)
{
	Vector *pointInControlCoordinates = new Vector();
	Control* deepestControl = rootContainer.getDeepestControlAt(point, pointInControlCoordinates);

	if(deepestControl != controlWithFocus)
	{
		controlWithFocus->mouseExited();
		deepestControl->mouseEntered();
	}
	controlWithFocus = deepestControl;

	Vector returnPoint = Vector(pointInControlCoordinates->getX(), pointInControlCoordinates->getY());
	delete pointInControlCoordinates;
	return returnPoint;
}

void UserInterface::processMouseMotionEvent(const SDL_MouseMotionEvent& event)
{
	MouseListener::EventArguments arguments(Vector(event.x, event.y),
			MouseListener::EventArguments::NONE);
	if(event.state == 1)
	{
		arguments.setButton(MouseListener::EventArguments::LEFT);
	}
	else if(event.state == 3)
	{
		arguments.setButton(MouseListener::EventArguments::RIGHT);
	}

	arguments.setPoint(switchFocusControlAndTransformToItsCoordinates(arguments.getPoint()));
	arguments.setSource(controlWithFocus);
	if(pressed)
	{
		controlWithFocus->mouseDragged(arguments);
	}
	else
	{
		controlWithFocus->mouseMoved(arguments);
	}
}

void UserInterface::processKeyEvent(const SDL_KeyboardEvent& event)
{
	controlWithFocus->keyPressed(createKeyArgumentsBasedOnEvent(event));
}

KeyListener::EventArguments UserInterface::createKeyArgumentsBasedOnEvent(
		const SDL_KeyboardEvent& event)
{
	SDLKey key = event.keysym.sym;
	char character = static_cast<char> (key);
	if(event.keysym.mod & KMOD_SHIFT)
	{
		character = toupper(character);
	}

	return KeyListener::EventArguments(key, character, controlWithFocus);
}

void UserInterface::updateWithDeltaTime(float deltaTimeInSeconds)
{
	World& world = World::getInstance();

	world.updateWithDeltaTime(controlPanel->getTimeSpeedMultiplication(), deltaTimeInSeconds);
	globalInfoPanel->update();

	if(selectElementMouseListener->anySelected())
	{
		const World::ElementsVector& elements = world.getElements();
		if(VectorUtils::findInSharedPtrVector<Element>(elements,
				selectElementMouseListener->getSelected()) == elements.end())
		{
			selectElementMouseListener->clearSelection();
		}
	}
	elementInfoPanel->updateFromListener(selectElementMouseListener);
}

void UserInterface::draw()
{
	const ClipArea& clipArea = ClipArea(rootContainer.getBounds(), 0, 0);
	ClipArea::rootClip = rootContainer.getBounds();
	rootContainer.draw(clipArea);
}

Container* UserInterface::createLeftPanel()
{
	static const geometry::Rectangle SIZE = geometry::Rectangle(Vector(0, 0), Dimension(
			LEFT_PANEL_WIDTH, windowSize.getHeight()));
	TabbedPane* tabbedPane = new TabbedPane;
	tabbedPane->setBounds(SIZE);

	tabbedPane->add(createInfoPanel(), "Info");
	tabbedPane->add(createControlPanel(), "Control");

	return tabbedPane;
}

Container* UserInterface::createInfoPanel()
{
	const Dimension infoPanelSize(LEFT_PANEL_WIDTH - ScrollBar::SIZE, windowSize.getHeight() / 2);
	Container* infoPanel = new Container;
	infoPanel->setSize(Dimension(LEFT_PANEL_WIDTH, windowSize.getHeight()));

	globalInfoPanel = new GlobalInfoPanel(infoPanelSize);
	globalInfoPanel->addManagedKeyListener(new StartStopWorldKeyListener());
	infoPanel->add(globalInfoPanel);

	elementInfoPanel = new ElementInfoPanel(infoPanelSize);
	elementInfoPanel->addManagedKeyListener(new StartStopWorldKeyListener());
	infoPanel->add(elementInfoPanel);

	return new ScrollableContainer(infoPanel);
}

Container* UserInterface::createControlPanel()
{
	const Dimension controlPanelSize(LEFT_PANEL_WIDTH - ScrollBar::SIZE, windowSize.getHeight()
			/ 2.0f);

	controlPanel = new ControlPanel(controlPanelSize, selectElementMouseListener);
	controlPanel->addManagedKeyListener(new StartStopWorldKeyListener());
	controlPanel->setSize(Dimension(LEFT_PANEL_WIDTH, windowSize.getHeight()));

	return new ScrollableContainer(controlPanel);
}

Container* UserInterface::createRightPanel()
{
	CreationState* creationState = controlPanel->getCreationState();
	worldViewControl = new WorldViewControl(selectElementMouseListener, creationState);

	worldViewControl->addManagedMouseListener(new AddElementToWorldMouseListener(
			controlPanel->getAddOnDragCheckbox(), controlPanel->getAnchorToGridCheckbox(),
			controlPanel->getCreationState()));
	worldViewControl->addManagedKeyListener(new StartStopWorldKeyListener());

	return new ScrollableContainer(worldViewControl);
}

